package net.qiqbframework.messaging.annotation;

import net.qiqbframework.common.sorting.AnnotatedPriorityComparator;


import javax.annotation.Nonnull;
import java.lang.reflect.Executable;
import java.util.*;

/**
 *
 *
 */
public class MultiHandlerDefinition implements MessageHandlingMemberFactory {

    private final List<MessageHandlingMemberFactory> handlerDefinitions;
    private final ExecutableHandlerWrapper handlerEnhancerDefinition;


    public static MultiHandlerDefinition ordered(MessageHandlingMemberFactory... delegates) {
        return ordered(Arrays.asList(delegates));
    }


    public static MultiHandlerDefinition ordered(ExecutableHandlerWrapper handlerEnhancerDefinition,
                                                 MessageHandlingMemberFactory... delegates) {
        return ordered(Arrays.asList(delegates), handlerEnhancerDefinition);
    }

    public static MultiHandlerDefinition ordered(List<MessageHandlingMemberFactory> delegates) {
        return new MultiHandlerDefinition(delegates);
    }

    public static MultiHandlerDefinition ordered(List<MessageHandlingMemberFactory> delegates,
                                                 ExecutableHandlerWrapper handlerEnhancerDefinition) {
        return new MultiHandlerDefinition(delegates, MultiExecutableHandlerEnhancer.ordered(handlerEnhancerDefinition));
    }

    public MultiHandlerDefinition(MessageHandlingMemberFactory... delegates) {
        this(Arrays.asList(delegates));
    }


    public MultiHandlerDefinition(List<MessageHandlingMemberFactory> delegates) {
        this(delegates,
             ClasspathHandlerEnhancerDefinition.forClassLoader(Thread.currentThread().getContextClassLoader()));
    }

    public MultiHandlerDefinition(List<MessageHandlingMemberFactory> delegates,
                                  ExecutableHandlerWrapper handlerEnhancerDefinition) {
        this.handlerDefinitions = flatten(delegates);
        this.handlerEnhancerDefinition = handlerEnhancerDefinition;
    }

    private static List<MessageHandlingMemberFactory> flatten(List<MessageHandlingMemberFactory> handlerDefinitions) {
        List<MessageHandlingMemberFactory> flattened = new ArrayList<>(handlerDefinitions.size());
        for (MessageHandlingMemberFactory handlerDefinition : handlerDefinitions) {
            if (handlerDefinition instanceof MultiHandlerDefinition) {
                flattened.addAll(((MultiHandlerDefinition) handlerDefinition).getDelegates());
            } else {
                flattened.add(handlerDefinition);
            }
        }
        flattened.sort(AnnotatedPriorityComparator.getInstance());
        return flattened;
    }

    public List<MessageHandlingMemberFactory> getDelegates() {
        return Collections.unmodifiableList(handlerDefinitions);
    }


    public ExecutableHandlerWrapper getHandlerEnhancerDefinition() {
        return handlerEnhancerDefinition;
    }

    @Override
    public <T> Optional<MessageExecutableHandler> createHandler(@Nonnull Class<T> declaringType,
                                                                @Nonnull Executable executable,
                                                                @Nonnull HandlerParameterResolverFactory parameterResolverFactory
            , @Nonnull HandlerAttributesPostProcessor handlerAttributesPostProcessor) {
        Optional<MessageExecutableHandler> handler = Optional.empty();
        for (MessageHandlingMemberFactory handlerDefinition : handlerDefinitions) {
            handler = handlerDefinition.createHandler(declaringType, executable, parameterResolverFactory,handlerAttributesPostProcessor);
            if (handler.isPresent()) {
                return Optional.of(handlerEnhancerDefinition.wrap(handler.get()));
            }
        }
        return handler;
    }
}
